import java.util.concurrent.*;
import java.util.concurrent.locks.*;

public class Thread {

    private static library library = new library();


    public static void main(String[] args) {
        System.out.println("-------------------");
        System.out.println("| 快递站存取快递流程  |");
        System.out.println("-------------------");
        //新建线程池，把存取快递任务输入进去
        ExecutorService executor = Executors.newFixedThreadPool(2);
        executor.execute(new returnTask());
        executor.execute(new boorroTask());

        //将线程池结束掉，释放资源
        executor.shutdown();
    }

    //新建任务类returnTask，此任务的run方法可执行静态类快递的入库方法，每次随机入库1-5个快递，运行100次
    public static class returnTask implements Runnable {
        @Override
        public void run() {
            int j = 0;
            try {
                do {
                    library.returnBook((int) (Math.random() * 5) + 1);  //每次随机入库1-5个快递
                    Thread.sleep(1000);  //每次入库休眠1秒钟再继续
                    j++;
                    //限定每天只有一百人的快递入库
                }
                while (j < 100);
            } catch (InterruptedException ex) {
                ex.printStackTrace(); //有线程阻塞异常时不会中断程序
            }
        }
    }

    //新建任务类boorroTask，此任务的run方法可执行静态类图书馆的取快递方法，每次随机取走1-5个快递，运行100次
    public static class boorroTask implements Runnable {
        public void run() {
            int i = 0;
            try {
                do {
                    library.borrowBooks((int) (Math.random() * 5) + 1);
                    Thread.sleep(1000); //每次取完快递休眠1秒钟
                    i++;
                    //限定每天只有一百人来取快递
                }
                while (i < 100);
            } catch (InterruptedException ex) {
                ex.printStackTrace(); //有线程阻塞异常时不会中断程序
            }
        }
    }

    //定义静态类：图书馆类library,里面有变量margin表示快递余量，一个取快递方法，一个快递入库方法
    public static class library {
        private static Lock lock = new ReentrantLock();   //创建一把锁

        private static Condition newDeposit = lock.newCondition();  //创建解锁条件

        private int margin; //快递余量

        public int getMargin() {
            return margin;
        } //获取快递余量

        //定义borrowBooks方法，此方法用于取快递
        public void borrowBooks(int num) {
            lock.lock(); //先上一把锁，防止同时取快递的事情发生

            try {
                while (margin < num) {       //如果快递余量小于要取快递个数就执行下面代码
                    System.out.println();
                    System.out.println("                                                                ");
                    System.out.println("\t\t\t 没有这么多书，先等一会吧");   //打印出来，表示快递还没入库，下次再来
                    System.out.println("                                                                ");
                    System.out.println();
                    newDeposit.await();      //把当前线程阻塞掉，暂停营业
                }
                margin -= num; //如果快递余量大于要取快递的个数，就把快递取出去，余量减去取快递的数量
                System.out.println();
                System.out.println("****************************************************************");
                System.out.println("\t\t\t 取走了" + num + "个快递" + "\t\t\t 还剩下"
                        + margin + "个快递");
                System.out.println("****************************************************************");
                System.out.println();

            } catch (InterruptedException ex) {
                ex.printStackTrace();    //有线程阻塞异常时不会中断程序
            } finally {
                lock.unlock();  //无论有没有取快递成功，都把锁打开
            }
        }

        //定义returnBook方法，此方法用于快递入库
        public void returnBook(int num) {
            lock.lock();   //快递入库前先上锁
            try {
                margin += num; //快递余量加上快递入库的数量
                System.out.println();
                System.out.println("----------------------------------------------------------------");
                System.out.println("快递入库成功，数量为：" + num + "\t\t\t 当前快递余量为："
                        + margin);
                System.out.println("----------------------------------------------------------------");
                System.out.println();
                newDeposit.signalAll();
            } finally {
                lock.unlock();   //无论结果如何，开锁
            }
        }
    }
}
